package Network;

import baseservergrid.ModeCompilation;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.RemoteStub;
import java.rmi.server.UnicastRemoteObject;
import javax.swing.JOptionPane;
import taskmanager.ITaskManager;

/**
 * Класс управляющий сетевым взамодействием сервер-сервер, автоматически идентифицирующий
 * роль сервера (арбитр или ведомый сервер)
 * @author Volk ID = 1001
 */
public class s2sInnerLogic implements IModeWork/*, ModeCompilation*/{

  //----------------------------Константы класса--------------------------------
   /** Путь до файла настроек */
  private static final String SETTINGS_PATH="./s2sconfig.ini";
   /** Формат файла настроек */
  private static final String FORMATV = "version 1.1";
   /**
    * Иия удалённой заглушки предоставляющие сервисы подчинённого сервера
    */
  private final String STUBSLAVENAME = "SlaveServerController";
  /**
   * Имя удалённой заглушки предоставляющей сервисы сервера арбитра
   */
  private final String STUBARBITRATORNAME = "ArbitrationController";

  //----------------------------Аттрибуты класса--------------------------------
  static int mode;
  static long GUIDServer = 0; //Уникальный идентификатор сервера
  private SlaveServerController slaveController = null; //Удалённый объект предоставляющий сервисы подчинённого сервера
  private ArbitrationController arbitrationController = null; //Удалённый объект предоставляющий сервисы сервера арбитра
  private ITaskManager taskManager; //планировщик
  //----------------------------------------------------------------------------

  //---------------------------Сервисные методы---------------------------------
  /**
   * Создание заглушки
   * @throws java.io.IOException - ошибка создания заглушки
   */
  private void createStub(String stubName) {

       try{
       System.out.println("Wait: creating slave server stub");
       File file = new File ("build"+File.separator+"classes");
       String command; //Команла на запуск утилиты tmic для генерации заглушки
       //System.out.println(System.getProperty("java.home"));
       command="\""+System.getProperty("java.home")+
               File.separator+".."+File.separator+"bin"+File.separator+"rmic.exe"+ "\" -classpath "+"\"" + file.getAbsolutePath() + "\""
              + " -d \""+ file.getAbsolutePath() + "\" " + "Network." + stubName;
       System.out.println("Run: "+command);
       Runtime.getRuntime().exec(command);

       System.out.println("Sleep 5 seconds while rmic generate the sub...");
       for(int i=5;i>0;i--){
           System.out.print(""+i+" ");
           Thread.currentThread().sleep(1000);
       }
       System.out.println("Wake up");
       }catch(IOException ioe){
        JOptionPane.showMessageDialog(null, "При попытке создание заглушки SlaveStub произошла ошибка - работа не гарантируется");
       }catch(InterruptedException ie){
        JOptionPane.showMessageDialog(null, "Процесс создания заглушки SlaveStub был прерван из вне - работа не гарантируется");
       }
  }

  /**
   * Преобразровать URL адрес в RMI адрес
   * @param inetAddress - адрес хоста
   * @param port - адрес порта
   * @param stubName - имя заглушки
   */
  private String transformToRMIURL(String inetAddress, int port, String stubName){
     inetAddress.trim();
     if(inetAddress.contains(File.separator + stubName)) inetAddress = inetAddress.substring(0, inetAddress.length() - stubName.length() - File.separator.length());
     inetAddress+= ":" + port + File.separator+ stubName;

     if( (!inetAddress.contains("http") && (!inetAddress.contains("rmi")) )) inetAddress = "rmi:" + File.separator + File.separator + inetAddress;
     else inetAddress =  inetAddress.replace("http", "rmi");
     return inetAddress;
  }
  //---------------------------Интерфейсные методы--------------------------------------------------
  /**
   * Создание объекта. В рамках инициализации межсерверного взаимодействия происходит регистрация
   * удалённого объекта SlaveServerController. Подключение происходит к порту по умолчанию
   * @throws java.io.IOException - доступ к файлу настроек логики межсетевого взаимодействия не возможен
  */
  public s2sInnerLogic(ITaskManager taskManager) throws IOException{
   //Если режим работы до сих пор не установлен, то устанавливаем режим работы по умолчанию
   if(mode == UNCHECKED) mode = AUTONOMIC_MODE;
   this.taskManager=taskManager;
   try{
    load();
   }catch(IOException ioe){
    save();
   }
  }

  /**
   * Активация серверных модулей
   * @return true - активация прошла успешно, false - ошибка при активации межсерверного взаимодействия
   */
  public boolean activation(){
    
    //---------------Создание заглушки подчинённого сервиса---------------------
     if(baseservergrid.ModeCompilation.DEBUG){
      createStub(STUBSLAVENAME); //Создание заглушки подчинённого сервера
     }
     try{
     System.out.println("Создание удалённого объекта SlaveStub");
     slaveController = new SlaveServerController(taskManager);
     RemoteStub slaveStub = (RemoteStub) UnicastRemoteObject.exportObject(slaveController,0);
     System.out.println("Binding.....");
     Registry stReg = LocateRegistry.getRegistry(NetworkLogic.getPort());
     stReg.rebind(STUBSLAVENAME, slaveStub);
     //----------------Создание заглушки сервера арбитра
     if(baseservergrid.ModeCompilation.DEBUG){
     createStub(STUBARBITRATORNAME); //Создание заглушки сервера арбитра
     }
     System.out.println("Создание удалённого объекта ArbitratorStub");
     arbitrationController = new ArbitrationController();
     RemoteStub arbitratorStub = (RemoteStub) UnicastRemoteObject.exportObject(arbitrationController,0);
     System.out.println("Binding.....");
     stReg.rebind(STUBARBITRATORNAME, arbitratorStub);
     }catch(IOException ioe){
      return false;
     }
     return true;
  }

    /**
     * Проверить является ли текущий сервер подчинённым
     * @return true - сервер является подчинённым, false - сервер не является подчинённым
     */
    public boolean isSlave(){return mode == SLAVE_MODE?true:false;}

    /**
     * Проверить является ли текущий арбитром
     * @return true - сервер является арбитром, false - сервер не является арбитром
    */
    public boolean isArbitrator(){return mode == ARBITRATOR_MODE?true:false;}

    /**
     * Получить уникальный идентификатор сервера
     * @return уникальный идентификатор сервера
     */
    public static long getGUID(){return GUIDServer;}

    /**
     * Получить удадённый интерфейс арбитра
     * @return удалённый интерфейс арбитра, null - сервер не является подчинённым
     * @throws java.rmi.RemoteException - разрыв соединения
     */
    public IArbitrationController getIArbitrator() throws RemoteException{
      return slaveController.getArbitratorController();
    }

    /**
     * Получить объект заглушки арбитра текущей машины
     * @return обхект заглушки арбитра для текущей машины
     */
    public ArbitrationController getMyArbitrationController(){
      return arbitrationController;
    }

    /**
     * Получить объект заглушки арбитра как подчинённого текущей машины
     * @return объект заглушки сервера как подчинённого текущей машины
     */
    public SlaveServerController getMySlaveController(){
     return slaveController;
    }

    /**
     * @author Volk ID = 1001
     * Сохранить параметры межсетевого взаимодействия в файле настроек
     * @throws java.io.IOException ошибка при работе с файлом
     * @version 2.0 Volk: В формат файла добавлены параметры текущего сервера
     *                    введено понятие формата файла (константа FORMATV)
     * @version 3.0 Volk: в формате файла остались лишь сведения о текущех параметрах сервера
     * @version 4.0 Volk: после изменение архитектуры сети, в данном модуле остались только параметры межсерверного взаимодействия
    */
    public static void save() throws IOException{
     //создать поток
       ObjectOutputStream stream = new ObjectOutputStream
                (new FileOutputStream(SETTINGS_PATH));
     //сохранить записи
       try{
       //Сохраняем формат файла
       stream.writeUTF(FORMATV);
       //Сохраняем параметры межсерверного взаимодействия
       stream.writeInt(s2sInnerLogic.mode);
       //Сохраняем уникальный идентификатор пользователя
       stream.writeLong(GUIDServer);
       }finally{stream.close();}
    }

   /**
   * @author Volk ID = 1001
   * Загрузить персональные данные из файла настроек
   * @throws java.io.IOException ошибка при работе с файлом (файл не найден, блокирован или имеет несовместимый или повреждённый формат)
   * @version 2.0 Volk: Добавлена отложенная загрузка с проверкой корректности формата
   *              , в случае если файл был поврежён или формат файла не соответсвует, то
   *              будет сгенерировано исключение, а текущие данные будут восстановлены.
   *              Введена проверка на совпадения версии форматов файла настроек
   * @versin 3.0  Volk: В формате файла остались лишь персональные настройки сервера
   * @version 4.0 Volk: после изменение архитектуры сети, в данном модуле остались только параметры межсерверного взаимодействия
   */
    public void load() throws IOException{
       //создать поток
       ObjectInputStream stream = new ObjectInputStream
            (new FileInputStream(SETTINGS_PATH));
       try{
       //Проверка на совпадение форматов
       String version = stream.readUTF();
       if(!version.equals(FORMATV)) throw new IOException();
       //Загружаем параметры межсерверного взаимодействия
       mode = stream.readInt();
       //Загружаем уникальный идентификатор пользователя
       GUIDServer = stream.readLong();
       }finally{stream.close();}
    }
    /**
     * Сохранить все сведения о текущей машине (как арбитра, как ведомого)
     * @throws java.io.IOException ошибка записи в файл
     */
    public void saveAll() throws IOException{
      save();
      if(arbitrationController != null) arbitrationController.saveAddresses();
      if(slaveController != null) slaveController.save();
    }



}
